package exchange

import (
	"context"
	"encoding/csv"
	"errors"
	"github.com/hylaz/tradingbot/database/model"
	"github.com/hylaz/tradingbot/pkg/mongoex"
	"go.mongodb.org/mongo-driver/v2/bson"
	"io"
	"os"
	"strings"

	"github.com/gofrs/uuid"
	"github.com/hylaz/tradingbot/common"
	"github.com/hylaz/tradingbot/common/cache"
	"github.com/hylaz/tradingbot/database"
	"github.com/hylaz/tradingbot/log"
)

// One returns one exchange by Name
func One(in string) (Details, error) {
	filter := bson.M{"uuid": in}
	var exchange model.Exchange
	err := mongoex.GetClient().Database("bot").Collection("exchange").FindOne(context.Background(), filter).Decode(&exchange)
	if err != nil {
		return Details{}, err
	}

	return Details{
		UUID: uuid.FromStringOrNil(exchange.ID),
		Name: exchange.Name,
	}, nil

}

// Insert writes a single entry into database
func Insert(in Details) error {
	exchange := model.Exchange{
		ID:   in.UUID.String(),
		Name: strings.ToLower(in.Name),
	}
	if mongoex.GetClient() == nil {
		return database.ErrDatabaseSupportDisabled
	}
	_, err := mongoex.GetClient().Database("bot").Collection("exchange").InsertOne(context.Background(), exchange)
	return err

}

// InsertMany writes multiple entries into database
func InsertMany(in []Details) error {
	if mongoex.GetClient() == nil {
		return database.ErrDatabaseSupportDisabled
	}
	var exchanges []model.Exchange
	for i := range in {
		exchange := model.Exchange{
			ID:   in[i].UUID.String(),
			Name: strings.ToLower(in[i].Name),
		}
		exchanges = append(exchanges, exchange)
	}
	if len(exchanges) != 0 {
		_, err := mongoex.GetClient().Database("bot").Collection("exchange").InsertMany(context.Background(), exchanges)
		return err
	}
	return errors.New("no exchanges")

}

// UUIDByName returns UUID of exchange
func UUIDByName(exchange string) (uuid.UUID, error) {
	exchange = strings.ToLower(exchange)
	v := exchangeCache.Get(exchange)
	if v != nil {
		u, ok := v.(uuid.UUID)
		if !ok {
			return uuid.UUID{}, common.GetTypeAssertError("uuid.UUID", v)
		}
		return u, nil
	}
	ret, err := One(exchange)
	if err != nil {
		return uuid.UUID{}, ErrNoExchangeFound
	}
	exchangeCache.Add(exchange, ret.UUID)
	return ret.UUID, nil
}

// ResetExchangeCache reinitialise cache to blank state used to clear cache for testing
func ResetExchangeCache() {
	exchangeCache = cache.New(10)
}

// LoadCSV loads & parses a CSV list of exchanges
func LoadCSV(file string) (out []Details, err error) {
	csvFile, err := os.Open(file)
	if err != nil {
		return out, err
	}

	defer func() {
		err = csvFile.Close()
		if err != nil {
			log.Errorln(log.Global, err)
		}
	}()

	csvData := csv.NewReader(csvFile)
	for {
		row, errCSV := csvData.Read()
		if errCSV != nil {
			if errCSV == io.EOF {
				return out, err
			}
			return out, errCSV
		}

		out = append(out, Details{
			Name: row[0],
		})
	}
}
